class Dep{ //被观察者
    constructor(){
        this.watchers = []//存放观察者的数组
    }
    on(watcher){ //订阅
        this.watchers.push(watcher)//把观察者放入watchers里
    }
    emit(){ //发布
        this.watchers.forEach(watcher => {
            watcher.update()
        })
    }
}
class Watcher{ //观察者 
    constructor(vm,value,cb){ //vm:vue实例 value:穿过来的值 cb：回调函数
        this.vm = vm
        this.value = value
        this.cb = cb
        this.initValue = this.get()//定义初始值
    }
    get(){//在被观察者的属性上把观察者放入
        Dep.target = this
        let value = CompileUtil.getValue(this.vm,this.value) // 获取值
        Dep.target = null// 释放观察者，清空被观察者的target
        return value//返回获取到的值
    }
    update(){// 更新值
        let value = CompileUtil.getValue(this.vm,this.value)// 获取值
        if(value !== this.initValue){//判断值和新值是否一样
            this.cb(value)//返回更新后的值
        }
    }
}
class Observer{//数据劫持
    constructor(data){
        this.observer(data)
    }
    observer(data){
        if(data && typeof data === 'object'){//判断数据类型
            for(let key in data){//循环调用observerNode
                this.observerNode(data,key,data[key])
            }
        }
    }
    observerNode(obj,key,value){//obj：data key：键名 value：对应的值
        this.observer(value)//递归调用
        let dep = new Dep()
        Object.defineProperty(obj,key,{
            get(){//第一次获取值的时候触发被观察者的订阅
                Dep.target && dep.on(Dep.target)
                return value
            },
            set:(newValue) => {
                if(value!==newValue){
                    this.observer(newValue)
                    value = newValue
                    dep.emit() //发布方法
                }
            }
        })
    }
}
class Compile{//指令解析
    constructor(el,vm){//el：传过来的东西 vm：vue实例
        this.vm = vm
        this.el = this.isElementNode(el)?el:document.querySelector(el)//判断el是否是节点
        let frament = this.node2Fragment(this.el)
        this.compile(frament) //编译{{school}} v-model
        this.el.append(frament)//将节点从内存中取出
    }
    isElementNode(node){
        return node.nodeType === 1  // 判断是否是元素节点
    }
    node2Fragment(node){//存储节点的方法
        let frament = document.createDocumentFragment()//创建内存碎片并放入
        let firstChild;
        while(firstChild=node.firstChild){
            frament.append(firstChild)//把firstChild放入碎片中
        }
        return frament//返回碎片
    }
    compile(node){// 编译方法
        let childNodes = node.childNodes; // 获得#app所有子节点 
        [...childNodes].forEach(item => {//遍历
            if(this.isElementNode(item)){//判断是元素节点
                this.compileNode(item) 
                this.compile(item) //  解构出{{school}} v-model
            }else{//判断是文本节点
                this.textNode(item) 
            }
        })
    }
    compileNode(node){ // 元素节点的编译
        let attributes = node.attributes;//获取节点上的属性
        [...attributes].forEach((item)=>{//遍历节点
            let {name,value} = item//name：type和v-model  value：text和school
            if(name.startsWith('v-')){//判断是不是v-开头
                let [,directive] = name.split('-')//截取方法
                CompileUtil[directive](node,value,this.vm)//把node和value传入
            }
        })
    }
    textNode(node){ // 编译文本
        let content = node.textContent;//
        if(/\{\{(.+?)\}\}/.test(content)){
            CompileUtil['text'](node,content,this.vm)
        }
    }
}
CompileUtil = {
    getValue(vm,value){//获取值的方法
        window.vm = vm 
        return value.split('.').reduce((prev,current)=>{
            return prev[current]
        },vm.data)
    },
    setValue(vm,value,data){ // 设置更改实例里的数
        value.split('.').reduce((prev,current,index,arr)=>{
            if(index===arr.length-1){
                // 最后一次赋值
                return prev[current] = data
            }
            return prev[current]
        },vm.data)
    },
    model(node,value,vm){ //解析指令的方法  node：input value:school vm：实例
        let valueData = this.getValue(vm,value)//获取值
        let fn = this.update['modelUpdate']
        new Watcher(vm,value,(newValue)=>{
            fn(node,newValue)//newValue是回调回来的值
        })
        fn(node,valueData)
        window.node = node
        node.addEventListener('input',(e)=>{//input事件
            let data = e.target.value//新输入的值
            this.setValue(vm,value,data)//表达式的值
        })
    },
    text(node,content,vm){ // 文本解析 node："{{school}}" content:{{school}} vm:实例
        let fn = this.update['textUpdate']
        let contentData = content.replace(/\{\{(.+?)\}\}/,(...args)=>{
            new Watcher(vm,args[1],(newValue)=>{
                fn(node,newValue)
            })
            return this.getValue(vm,args[1])
        })
        fn(node,contentData)
    },
    update: {
        modelUpdate(node,valueData){// node：input标签 valueData：传过来的值
            node.value = valueData  //替换value
        },
        textUpdate(node,contentData){// node：字符串school contentData：八维教育
            node.textContent = contentData
        }
    }
}
class Vue{//创建vue对象
    constructor(options={}){
        this.el = options.el
         this.data = options.data
        if(this.el){
            // 数据劫持
            new Observer(this.data)
            // 指令解析
            new Compile(this.el,this)
        }
    }
}